package com.quiz.bis.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.quiz.bis.domain.bo.ExamConfig;
import com.quiz.bis.domain.vo.ExamMemberVo;
import com.quiz.bis.domain.vo.ExamSessionVo;
import com.quiz.common.core.domain.R;
import com.quiz.common.core.domain.entity.SysUser;
import com.quiz.common.mybatis.core.page.PageQuery;
import com.quiz.common.mybatis.core.page.TableDataInfo;
import com.quiz.bis.domain.bo.ExamBo;
import com.quiz.bis.domain.model.*;
import com.quiz.bis.domain.vo.ExamDetailVo;
import com.quiz.bis.domain.vo.ExamVo;
import com.quiz.bis.mapper.*;
import com.quiz.bis.service.IExamService;
import com.quiz.common.mybatis.core.query.PlusWrappers;
import com.quiz.common.utils.SecurityUtils;
import com.quiz.common.utils.bean.BeanUtils;
import com.quiz.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ExamServiceImpl implements IExamService {
    private final ExamMapper examMapper;
    private final ExamSubgroupMemberMapper subgroupMemberMapper;
    private final ExamStudentRecordMapper studentRecordMapper;
    private final ExamGroupMemberMapper groupMemberMapper;
    private final ExamParticipantMapper examParticipantMapper;
    private final ExamSessionMapper examSessionMapper;
    private final PaperQuestionMapper paperQuestionMapper;
    private final SysUserMapper sysUserMapper;
    private final PaperMapper paperMapper;

    @Override
    public TableDataInfo<ExamVo> pageExamList(PageQuery pageQuery, Long groupId, Integer type) {
        Exam exam = new Exam();
        exam.setExamGroupId(groupId);
        exam.setType(type);
        IPage<ExamVo> page = examMapper.selectExamPage(pageQuery.build(), exam);
        return TableDataInfo.build(page);
    }

    @Override
    public ExamDetailVo getExamDetail(Long id) {
        ExamVo examVo = examMapper.selectExamDetail(id);
        if (examVo == null) {
            return null;
        }


        ExamDetailVo detailVo = new ExamDetailVo();
        // 复制属性
        BeanUtils.copyProperties(examVo, detailVo);
        detailVo.setId(examVo.getId());
        detailVo.setExamGroupId(examVo.getExamGroupId().toString());
        detailVo.setStartTime(examVo.getStartTime().toString());
        detailVo.setEndTime(examVo.getEndTime().toString());
        ExamConfig configJson = JSONUtil.toBean(examVo.getConfigJson(), ExamConfig.class);
        detailVo.setConfigJson(configJson);

        ExamParticipant participant = examParticipantMapper.selectOne(
                PlusWrappers.lambdaQuery(ExamParticipant.class)
                        .eq(ExamParticipant::getExamId, id)
        );
        detailVo.setSubgroupIds(JSONUtil.parseArray(participant.getSubgroupIds()).toList(String.class));
        List<Long> userIds = JSONUtil.parseArray(participant.getExcludedUserIds()).toList(Long.class);
        if (CollUtil.isNotEmpty(userIds)) {
            List<SysUser> sysUsers = sysUserMapper.selectUserByIds(userIds);
            detailVo.setExcludedUsers(sysUsers);
        } else {
            detailVo.setExcludedUsers(new ArrayList<>());
        }

        // 设置试卷信息
        if (examVo.getPaperId() != null) {
            Paper paper = paperMapper.selectById(examVo.getPaperId());
            Long count = paperQuestionMapper.selectCount(
                    PlusWrappers.lambdaQuery(PaperQuestion.class)
                            .eq(PaperQuestion::getPaperId, examVo.getPaperId())
            );
            paper.setQuestionCount(count.toString());
            detailVo.setPaper(paper);
        }

        // 设置场次信息
        List<ExamSession> sessions = examSessionMapper.selectList(
                Wrappers.lambdaQuery(ExamSession.class)
                        .eq(ExamSession::getExamId, id)
        );
        detailVo.setSessions(convertToSessionVos(sessions));

        return detailVo;
    }

    @Override
    @Transactional
    public R<Long> createExam(ExamBo examBo) {

        // 验证时间
        if (examBo.getType() != 3) { // 非作业类型需要验证时间
            LocalDateTime now = LocalDateTime.now();
            if (examBo.getStartTime() != null && examBo.getStartTime().toInstant().isBefore(now.atZone(ZoneId.systemDefault()).toInstant())) {
                return R.fail("开始时间不能早于当前时间");
            }
            if (examBo.getEndTime() != null && examBo.getEndTime().toInstant().isBefore(now.atZone(ZoneId.systemDefault()).toInstant())) {
                return R.fail("结束时间不能早于当前时间");
            }
            if (examBo.getStartTime() != null && examBo.getEndTime() != null
                    && !examBo.getEndTime().toInstant().isAfter(examBo.getStartTime().toInstant())) {
                return R.fail("结束时间必须晚于开始时间");
            }
        }
        // 1. 保存考试基本信息
        Exam exam = BeanUtil.copyProperties(examBo, Exam.class);
        BeanUtil.copyProperties(examBo, exam);
        exam.setStatus(0); // 默认草稿状态
        exam.setCreateBy(com.quiz.common.utils.SecurityUtils.getUserId().toString());
        exam.setCreateTime(new Date());
        exam.setConfigJson(JSONUtil.toJsonStr(examBo.getConfigJson()));
        List<Long> joinExamStudentIds = null;
        if (CollUtil.isNotEmpty(examBo.getSubgroupIds())) {
            joinExamStudentIds = subgroupMemberMapper.selectJoinExamStudentIdList(examBo.getSubgroupIds(), examBo.getExcludedUserIds());
        } else { // 否则全体考试组成员参加考试
            joinExamStudentIds = groupMemberMapper.selectStuMemberList(examBo.getExamGroupId(), examBo.getExcludedUserIds());
        }
        exam.setJoinCount(joinExamStudentIds.size());

        examMapper.insert(exam);

        // 2. 保存参与配置
        saveParticipantConfig(exam.getId(), examBo.getSubgroupIds(), examBo.getExcludedUserIds());

        // 3. 如果是考试类型，创建默认场次
        if (examBo.getType() == 1 && examBo.getStartTime() != null && examBo.getEndTime() != null) {
            createDefaultSession(exam.getId(), examBo.getStartTime(), examBo.getEndTime());
        }


        return R.ok(exam.getId());
    }

    @Override
    @Transactional
    public R<Void> updateExam(ExamBo examBo) {
        Exam existingExam = examMapper.selectById(examBo.getId());
        if (existingExam == null) {
            return R.fail("考试不存在");
        }

        // 只有草稿状态可以编辑
        if (existingExam.getStatus() != 0) {
            return R.fail("只有草稿状态的考试可以编辑");
        }

        // 验证时间
        if (examBo.getType() != 3) {
            LocalDateTime now = LocalDateTime.now();
            if (examBo.getStartTime() != null && examBo.getStartTime().toInstant().isBefore(now.atZone(ZoneId.systemDefault()).toInstant())) {
                return R.fail("开始时间不能早于当前时间");
            }
        }
        // 1. 更新考试基本信息
        Exam exam = new Exam();
        BeanUtils.copyProperties(examBo, exam);
        exam.setStatus(0); // 默认草稿状态
        exam.setUpdateBy(SecurityUtils.getUserId().toString());
        exam.setUpdateTime(new Date());
        exam.setConfigJson(JSONUtil.toJsonStr(examBo.getConfigJson()));
        List<Long> joinExamStudentIds = null;
        if (CollUtil.isNotEmpty(examBo.getSubgroupIds())) {
            joinExamStudentIds = subgroupMemberMapper.selectJoinExamStudentIdList(examBo.getSubgroupIds(), examBo.getExcludedUserIds());
        } else { // 否则全体考试组成员参加考试
            joinExamStudentIds = groupMemberMapper.selectStuMemberList(examBo.getExamGroupId(), examBo.getExcludedUserIds());
        }

        exam.setJoinCount(joinExamStudentIds.size());


        examMapper.updateById(exam);

        // 2. 更新参与配置
        examParticipantMapper.deleteByExamId(examBo.getId());
        saveParticipantConfig(examBo.getId(), examBo.getSubgroupIds(), examBo.getExcludedUserIds());

        // 3. 更新场次信息（如果是考试类型）
        if (examBo.getType() == 1) {
            examSessionMapper.deleteByExamId(examBo.getId());
            if (examBo.getStartTime() != null && examBo.getEndTime() != null) {
                createDefaultSession(examBo.getId(), examBo.getStartTime(), examBo.getEndTime());
            }
        }

        // 4. 批量删除学生考试初始记录
        studentRecordMapper.deleteIgnoreLogicByExamIds(examBo.getId().toString());

        return R.ok();
    }

    @Override
    @Transactional
    public R<Void> deleteExam(Long id) {
        // 1. 删除考试基本信息（逻辑删除）
        examMapper.deleteById(id);

        // 2. 删除参与配置（逻辑删除）
        examParticipantMapper.delete(
                Wrappers.lambdaQuery(ExamParticipant.class)
                        .eq(ExamParticipant::getExamId, id)
        );

        // 3. 删除场次信息（逻辑删除）
        examSessionMapper.delete(
                Wrappers.lambdaQuery(ExamSession.class)
                        .eq(ExamSession::getExamId, id)
        );

        return R.ok();
    }

    @Override
    @Transactional
    public R<Void> updateStatusBatch(List<Long> ids, Integer status) {
        if (ids == null || ids.isEmpty()) {
            return R.fail("请选择要操作的活动");
        }

        List<Exam> exams = examMapper.selectBatchIds(ids);
        LocalDateTime now = LocalDateTime.now();

        for (Exam exam : exams) {
            // 发布操作验证
            if (status == 1) {
                if (exam.getStatus() != 0) {
                    return R.fail("只有草稿状态的考试可以发布");
                }
                if (exam.getStartTime() != null && exam.getStartTime().toInstant().isBefore(now.atZone(ZoneId.systemDefault()).toInstant())) {
                    return R.fail("考试已经开始，不能发布，请重新编辑考试时间");
                }
            }
            // 撤回操作验证
            else if (status == 0) {
                if (exam.getStatus() != 1) {
                    return R.fail("只有已发布状态的考试可以撤回");
                }
                if (exam.getStartTime() != null && exam.getStartTime().toInstant().isBefore(now.atZone(ZoneId.systemDefault()).toInstant())) {
                    return R.fail("已开始的考试不能撤回");
                }
            }
        }

        Exam exam = new Exam();
        exam.setStatus(status);
        exam.setUpdateBy(SecurityUtils.getUserId().toString());
        exam.setUpdateTime(new Date());

        // 如果状态为0，表示已经发布过又重新撤回，直接删除学生考试记录
        if (status == 0) {
            studentRecordMapper.deleteIgnoreLogicByExamIds(CollUtil.join(ids, ","));
        }
        // 如果状态为1，表示直接发布 或 撤回后重新发布，直接批量插入
        else {
            List<Long> examIds = exams.stream().map(Exam::getId).collect(Collectors.toList());
            List<ExamParticipant> examParticipants = examParticipantMapper.selectList(
                    PlusWrappers.lambdaQuery(ExamParticipant.class)
                            .in(ExamParticipant::getExamId, examIds)
            );
            // 收集所有的分组ID
            Set<Long> subGroupIds = examParticipants.stream()
                    .flatMap(e -> {
                        return Arrays.stream(e.getSubgroupIds().replaceAll("\\[|\\]", "").split(","));
                    }).filter(StrUtil::isNotEmpty)
                    .map(Long::parseLong)
                    .collect(Collectors.toSet());

            // 查询所有分组成员,并进行分组处理
            Map<Long, List<ExamSubgroupMember>> subgroupMembersMap = null;
            if (CollUtil.isNotEmpty(subGroupIds)) {
                List<ExamSubgroupMember> subgroupMembers = subgroupMemberMapper.selectList(
                        PlusWrappers.lambdaQuery(ExamSubgroupMember.class)
                                .in(ExamSubgroupMember::getSubgroupId, subGroupIds)
                );
                subgroupMembersMap = subgroupMembers.stream()
                        .collect(Collectors.groupingBy(ExamSubgroupMember::getSubgroupId));
            }
            // 查询出所有考试组成员
            Exam firstExam = exams.get(0);
            List<Long> groupMemberIds = groupMemberMapper.selectStuMemberList(firstExam.getExamGroupId(), null);
            List<ExamStudentRecord> studentRecords = new ArrayList<>();

            // 将考试列表分发给对应的考试成员
            for (ExamParticipant e : examParticipants) {
                List<Long> subgroupIds = JSONUtil.parseArray(e.getSubgroupIds()).toList(Long.class);
                Set<Long> excludedIds = new HashSet<>(JSONUtil.parseArray(e.getExcludedUserIds()).toList(Long.class));
                if (CollUtil.isNotEmpty(subgroupIds)) {
                    for (Long subgroupId : subgroupIds) {
                        List<ExamSubgroupMember> examSubgroupMembers = subgroupMembersMap.get(subgroupId);
                        for (ExamSubgroupMember examSubgroupMember : examSubgroupMembers) {
                            if (excludedIds.contains(examSubgroupMember.getUserId())) {
                                continue;
                            }
                            ExamStudentRecord studentRecord = new ExamStudentRecord();
                            studentRecord.setUserId(examSubgroupMember.getUserId());
                            studentRecord.setExamId(e.getExamId());
                            studentRecord.setStatus(0); // 默认为未参加
                            studentRecords.add(studentRecord);
                        }
                    }
                } else {
                    for (Long userId : groupMemberIds) {
                        if (excludedIds.contains(userId)) {
                            continue;
                        }
                        ExamStudentRecord studentRecord = new ExamStudentRecord();
                        studentRecord.setUserId(userId);
                        studentRecord.setExamId(e.getExamId());
                        studentRecord.setStatus(0);
                        studentRecords.add(studentRecord);
                    }
                }
            }
            studentRecordMapper.insertBatch(studentRecords);
        }


        examMapper.update(exam, Wrappers.lambdaUpdate(Exam.class)
                .in(Exam::getId, ids));

        return R.ok();
    }

    @Override
    public TableDataInfo<SysUser> pageJoinExanStudentList(PageQuery pageQuery, String examId, String groupId) {
        ExamParticipant examParticipant = examParticipantMapper.selectOne(
                Wrappers.lambdaQuery(ExamParticipant.class)
                        .eq(ExamParticipant::getExamId, Long.parseLong(examId))
        );
        List<Long> subgroupIds = JSONUtil.parseArray(examParticipant.getSubgroupIds()).toList(Long.class);
        List<Long> excludedUserIds = JSONUtil.parseArray(examParticipant.getExcludedUserIds()).toList(Long.class);

        List<Long> ids = null;
        if (CollUtil.isNotEmpty(subgroupIds)) {
            ids = subgroupMemberMapper.selectJoinExamStudentIdList(subgroupIds, excludedUserIds);
        } else {
            ids = groupMemberMapper.selectStuMemberList(Long.parseLong(groupId), excludedUserIds);
        }

        if (CollUtil.isEmpty(ids)) {
            return TableDataInfo.build(new ArrayList<>());
        }
        Page<SysUser> sysUserPage = sysUserMapper.selectUserByIds(pageQuery.build(), ids);
        return TableDataInfo.build(sysUserPage);
    }

    private void saveParticipantConfig(Long examId, List<Long> subgroupIds, List<Long> excludedUserIds) {
        ExamParticipant participant = new ExamParticipant();
        participant.setExamId(examId);
        participant.setSubgroupIds(JSONUtil.toJsonStr(subgroupIds));
        participant.setExcludedUserIds(JSONUtil.toJsonStr(excludedUserIds));
        participant.setCreateBy(com.quiz.common.utils.SecurityUtils.getUserId().toString());
        participant.setCreateTime(new Date());
        examParticipantMapper.insert(participant);
    }

    private void createDefaultSession(Long examId, Date startTime, Date endTime) {
        ExamSession session = new ExamSession();
        session.setExamId(examId);
        session.setSessionName("默认场次");
        session.setStartTime(startTime);
        session.setEndTime(endTime);
        session.setCreateBy(SecurityUtils.getUserId().toString());
        session.setCreateTime(new Date());
        examSessionMapper.insert(session);
    }

    private List<ExamSessionVo> convertToSessionVos(List<ExamSession> sessions) {
        return sessions.stream().map(session -> {
            ExamSessionVo vo = new ExamSessionVo();
            BeanUtils.copyProperties(session, vo);
            return vo;
        }).collect(Collectors.toList());
    }
}